/** @file
@brief Contains declarations related to #PrynFactory. */
#ifndef PRYN_FACTORY_H
#define PRYN_FACTORY_H

typedef struct PrynFactory PrynFactory; ///< A generator for components.

#include <pryn/platform.h>

typedef PrynDList (PrynFactory) PrynFactoryList; ///< List of factories.

#include <pryn/component.h>
#include <pryn/library.h>
#include <pryn/mediator.h>
#include <pryn/pin.h>
#include <pryn/result.h>
#include <pryn/state.h>
#include <pryn/string.h>
#include <pryn/tag.h>
#include <pryn/type.h>
#include <pryn/value.h>

/** @defgroup PrynFactory PrynFactory C API
	@{ */

 PrynCallbackTypedef (PrynResult, PrynFactoryCreateFunction, (PrynFactory *factory, PrynComponent **output, void *data))
	/**< @brief Function signature to create a component from a factory. 

	@param factory The factory registered with this function.
	@param[out] output A pointer to receive the newly-created component.
	@returns #PrynResultSuccess on success, or an error code; see #PrynResult. */

PrynCallbackTypedef (PrynResult, PrynFactoryDisplayNameFunction, (PrynComponent *component, PrynString *value, void *data))
	/**< @brief Function signature to retrieve the name to display in a board editor for a component.

	@param component The component whose factory is registered with this function.
	@param[out] value Returned value. This string must be valid until the next call to this function on this component, or until the component is destroyed.
	@returns #PrynResultSuccess on success, or an error code; see #PrynResult. */

PrynCallbackTypedef (PrynResult, PrynFactoryClickFunction, (PrynComponent *component, void *data))
	/**< @brief Function signature for when a component has been clicked on by the mouse; some simple action should be performed, if there are any to do so.
	
	@param component The component whose factory is registered with this function.
	@returns #PrynResultSuccess if something is done, #PrynResultDone if nothing happens, or an error code; see #PrynResult. */

PrynCallbackTypedef (PrynResult, PrynFactoryPinConnectedFunction, (PrynPin *pin, void *data))
	/**< @brief Function signature for notification that a pin has been connected to something else.

	@param pin The pin, whose component's factory is registered with this function.
	@returns #PrynResultSuccess or an error code; see #PrynResult. */

PrynCallbackTypedef (PrynResult, PrynFactoryPinDisconnectedFunction, (PrynPin *pin, void *data))
	/**< @brief Function signature for notification that a pin has been disconnected.

	@param pin The pin whose component's factory is registered with this function.
	@returns #PrynResultSuccess or an error code; see #PrynResult. */

PrynCallbackTypedef (PrynResult, PrynFactoryPinChangeValueFunction, (PrynPin *pin, void *data))
	/**< @brief Function signature for notification that a pin's value has changed in place.

	@param pin The pin whose component's factory is registered with this function.
	@returns #PrynResultSuccess or an error code; see #PrynResult. */

PrynImport (PrynResult, prynFactoryCreate, (PrynFactory **output, PrynLibrary *library, const PrynString *id))
	/**< @brief Create a new factory or return an existing factory.

	@param[out] output Written with the resultant factory, or 0 on failure.
	@param library The library this factory should be from.
	@param id The unique identifier of the factory.
	@returns #PrynResultSuccess or an error code; see #PrynResult. */

PrynImport (PrynResult, prynFactoryLoad, (PrynFactory *factory))
	/**< @brief Ensure that the factory is loaded, loading it if necessary.

	@param factory The factory to load.
	@returns #PrynResultSuccess or an error code; see #PrynResult. */

PrynImport (PrynResult, prynFactoryFind, (PrynFactory **output, PrynState *state, const PrynString *id))
	/**< @brief Find the factory with the given identifier in the state.

	@param[out] output Written with the found factory, or 0 on failure.
	@param state The state to search for the factory in.
	@param id The unique identifier of the factory.
	@returns #PrynResultSuccess if found, #PrynResultDone if not found, or an error code; see #PrynResult. */

PrynImport (PrynResult, prynFactoryDestroy, (PrynFactory *factory))
	/**< @brief Completely destroy a factory.

	Any components using this factory will be disassociated; see #prynComponentDisassociate. As such it's usually best to call this before any components are created.
	
	@param factory The factory to destroy. Using this pointer beyond this function call will result in program corruption.
	@returns #PrynResultSuccess or an error code; see #PrynResult. */

PrynImport (PrynResult, prynFactoryState, (PrynFactory *factory, PrynState **output)) ///< Return the state for a factory.
PrynImport (PrynResult, prynFactoryName, (PrynFactory *factory, PrynString *output)) ///< Return the name of the factory.
PrynImport (PrynResult, prynFactoryDescription, (PrynFactory *factory, PrynString *output)) ///< Return the description of the factory.
PrynImport (PrynResult, prynFactoryTags, (PrynFactory *factory, PrynTagList **output)) ///< List of tags associated with the factory.

PrynImport (PrynResult, prynFactorySetName, (PrynFactory *factory, const PrynString *value)) ///< Set the name for the factory. The value is duplicated off.
PrynImport (PrynResult, prynFactorySetDescription, (PrynFactory *factory, const PrynString *value)) ///< Set the description for the factory. The value is duplicated off.
PrynImport (PrynResult, prynFactorySetProprietary, (PrynFactory *factory, void *value)) ///< Set the proprietary value for the factory.
PrynImport (PrynResult, prynFactorySetMediator, (PrynFactory *factory, PrynMediator *value)) ///< Set the mediator for the factory.

/// Set the name of the factory to a literal string.
#define prynFactorySetNameLiteral(FACTORY, NAME) prynStringLiteralBlock (NAME, STRING, { prynFactorySetName ((FACTORY), &STRING); })

/// Set the description of the factory to a literal string.
#define prynFactorySetDescriptionLiteral(FACTORY, NAME) prynStringLiteralBlock (NAME, STRING, { prynFactorySetDescription ((FACTORY), &STRING); })

PrynImportSetCallback (, Factory, CreateFunction) ///< Set the create function for the factory.
PrynImportSetCallback (, Factory, DisplayNameFunction) ///< Set the display name function for the factory.
PrynImportSetCallback (, Factory, ClickFunction) ///< Set the click function for the factory.
PrynImportSetCallback (, Factory, PinConnectedFunction) ///< Set the notification function for when a pin has been connected.
PrynImportSetCallback (, Factory, PinDisconnectedFunction) ///< Set the notification function for when a pin has been disconnected.
PrynImportSetCallback (, Factory, PinChangeValueFunction) ///< Set the notification function for when a pin's value has been changed in place. This can be 0.

#ifdef PrynInternalStructs
/** @brief A generator for components.
*/
struct PrynFactory
{
#ifdef PrynInternal
/** @name Internal fields
	These fields are not normally visible to clients, and it is recommended not to use them to maximise binary compatibility. To make them available, define PrynInternal.
	@{ */

	PrynCommonObject pCommonObject; ///< Common fields you'd expect in an object, like tags and monitors.
	PrynCommonResource pCommonResource; ///< Common fields you'd expect in a resource, like name and description.

	PrynFactory *pNext; /**< Next factory in the list or 0 if this is the last. */
	PrynFactory *pPrevious; /**< Previous factory in the list or 0 if this is the first. */

	PrynComponentList pComponents; ///< List of components using this factory.

	PrynState *pState; ///< State the factory is in.
	PrynLibrary *pLibrary; ///< Library this factory is from.
	PrynMediator *pMediator; ///< Mediator this factory is from, or 0 for none.

	PrynString pId; ///< Unique identifier of the factory.
	
	PrynType **pInputs; ///< Types of the input pins. The actual component may have more pins; this is only a sample for the sake of generating linkages.
	size_t pInputCount; ///< Number of input pin types.
	PrynType **pOutputs; ///< Types of the output pins. The actual component may have more pins; this is only a sample for the sake of generating linkages.
	size_t pOutputCount; ///< Number of output pin types.
	
	void *pProprietary; ///< Proprietary data for the factory type.
	
	PrynCallback (PrynFactoryCreateFunction, pCreateFunction); ///< Create a new component; this is null if the factory is not loaded.
	
	PrynCallback (PrynFactoryDisplayNameFunction, pDisplayNameFunction); ///< Retrieve the name to display in the board editor.
	PrynCallback (PrynFactoryClickFunction, pClickFunction); ///< The component has been clicked on by the mouse; some simple action should be performed, if any. PrynResultDone should be returned if no action is performed.
	
	PrynCallback (PrynFactoryPinConnectedFunction, pPinConnectedFunction); ///< Notification that the pin has been connected.
	PrynCallback (PrynFactoryPinDisconnectedFunction, pPinDisconnectedFunction); ///< Notification that the pin has been disconnected.
	PrynCallback (PrynFactoryPinChangeValueFunction, pPinChangeValueFunction); ///< Notification that the pin's value has changed in place. This can be 0.

/** @} */
#endif /* PrynInternal */

#if __cplusplus
	static inline PrynFactory *Create (PrynLibrary *library, const PrynString &id) { PrynFactory *result; library->checkError (prynFactoryCreate (&result, library, &id)); return result; }
		/**< @brief Create a new factory or return an existing factory.

		@param library The library this factory should be from.
		@param id The unique identifier of the factory.
		@returns The created factory. */

	static inline PrynFactory *Find (PrynState *state, const PrynString &id) { PrynFactory *result; state->checkError (prynFactoryFind (&result, state, &id)); return result; }
		/**< @brief Find the factory with the given identifier in the state.

		@param state The state to search for the factory in.
		@param id The unique identifier of the factory.
		@returns The factory if found, or 0 if not found. */

	inline void load () { checkError (prynFactoryLoad (this)); }
		/**< Ensure that the factory is loaded, loading it if necessary. */

	inline PrynComponent *createComponent ();
		/**< Create a component from the factory.

		@returns The newly-created component. */

	inline PrynComponent *allocateComponent (size_t proprietarySize = 0);
		/**< @brief Allocate a component and clear it to normal defaults.

		This is called by factories, not clients. Use #createComponent to create a component using the factory.

		@param proprietarySize Number of bytes to allot to the proprietary data or 0 to use none. If non-zero this data is after the end of the component.
		@returns The newly-allocated component. */

/** @name Properties
	@{ */

	inline PrynState *state () { PrynState *output; prynCheckErrorNull (prynFactoryState (this, &output)); return output; } ///< Return the state for the factory.
	inline PrynString name () { PrynString output; checkError (prynFactoryName (this, &output)); return output; } ///< The name of the factory.
	inline void name (const PrynString &value) { checkError (prynFactorySetName (this, &value)); } ///< Set the name of the factory; the value is not copied.
	inline PrynString description () { PrynString output; checkError (prynFactoryDescription (this, &output)); return output; } ///< The description of the factory.
	inline void description (const PrynString &value) { checkError (prynFactorySetDescription (this, &value)); } ///< Set the description of the factory; the value is not copied.
	inline PrynTagList *tags () { PrynTagList *output; checkError (prynFactoryTags (this, &output)); return output; } ///< The tags associated with the factory.
	inline void proprietary (void *value) { checkError (prynFactorySetProprietary (this, value)); } ///< Set the proprietary data pointer for the factory.
	inline void mediator (PrynMediator *value) { checkError (prynFactorySetMediator (this, value)); } ///< Set the mediator for the factory.

/** @} */

/** @name Callbacks
@{ */

	PrynReturnCallbackWrapperDeclaration (Factory, CreateFunction, createFunction, this->state (), (PrynFactory *factory, PrynComponent **output, void *data), (factory, data), (PrynFactory *factory, void *data), PrynComponent *, output) ///< Set the creation function for the factory.

	PrynReturnCallbackWrapperDeclaration (Factory, DisplayNameFunction, displayNameFunction, this->state (), (PrynComponent *component, PrynString *value, void *data), (component, data), (PrynComponent *component, void *data), PrynString, value) ///< Set the display name function for the factory.
	PrynCallbackWrapperDeclaration (Factory, ClickFunction, clickFunction, this->state (), (PrynComponent *component, void *data), (component, data)) ///< Set the click function for the factory.
	PrynCallbackWrapperDeclaration (Factory, PinConnectedFunction, pinConnectedFunction, this->state (), (PrynPin *pin, void *data), (pin, data)) ///< Set the function to be notified of when the pin has been connected.
	PrynCallbackWrapperDeclaration (Factory, PinDisconnectedFunction, pinDisconnectedFunction, this->state (), (PrynPin *pin, void *data), (pin, data)) ///< Set the function to be notified of when the pin has been disconnected.
	PrynCallbackWrapperDeclaration (Factory, PinChangeValueFunction, pinChangeValueFunction, this->state (), (PrynPin *pin, void *data), (pin, data)) ///< Set the function to be notified of when the pin has changed value. This can be 0.

	inline PrynResult checkError (PrynResult result) { return prynCheckErrorBase (state (), result); } ///< If the result is below zero, throw an error.*/

/** @} */

#endif /* __cplusplus */
};
#endif /* PrynInternalStructs */

#if __cplusplus
/** @brief A virtual class wrapper for a factory. You can subclass this and overload member functions, as opposed to creating a normal factory and assigning callbacks. */
class PrynFactoryClass
{
protected:
	PrynFactory *pBase; ///< Link to the actual factory object.

public:
	virtual PrynString id () const = 0; ///< The globally-unique identifier of the factory.
	virtual PrynString defaultName () { return ""; } ///< Return the default name of the factory, which can be overwritten by using the "name" property.
	virtual PrynString defaultDescription () { return ""; } ///< Return the default description of the factory, which can be overwritten by using the "description" property.

	PrynFactoryClass () : pBase (0) { }

	/// Assign the base factory.
	virtual void construct (PrynLibrary *library);

	virtual ~PrynFactoryClass ()
	{
	}

	/** @name Properties
	@{ */

	PrynState *state () { return pBase->state (); } ///< The state the factory exists within.
	PrynString name () { return pBase->name (); } ///< The name of the factory. Initially this is the same as #defaultName ().
	void name (const PrynString &value) { pBase->name (value); } ///< Set the name of the factory.
	PrynString description () { return pBase->description (); } ///< The description of the factory. Initially this is the same as #defaultDescription ().
	void description (const PrynString &value) { pBase->description (value); } ///< Set the description of the factory.

	/** @} */ // (Properties section end)

	PrynComponent *allocateComponent (size_t proprietarySize = 0) { return pBase->allocateComponent (proprietarySize); } ///< Allocate a component assigned to this factory. This is to be used in #createFunction almost exclusively.

protected:
	/// This function is called to create a component from this factory.
	virtual PrynComponent *createFunction ()
	{
		throw PrynException (state (), PrynResultUnimplemented);
		return 0;
	}

	/// Return the name to display to an editor for this component. The default just returns #name ().
	virtual PrynString displayNameFunction (PrynComponent *component)
	{
		return name ();
	}

	/// Notification that the user has clicked on the component in an editor. Whether this has an action depends upon the component type.
	virtual void clickFunction (PrynComponent *component)
	{
	}

	/// Notification that the pin's value has been changed.
	virtual void pinChangeValueFunction (PrynPin *pin)
	{
	}

	/// Notification that the pin has been connected to another pin.
	virtual void pinConnectedFunction (PrynPin *pin)
	{
	}

protected:
	static inline PrynComponent *createFunctionStatic (PrynFactory *factory, void *data)
	{
		PrynFactoryClass *self = (PrynFactoryClass *) data;
		return self->createFunction ();
	}

	static inline void pinChangeValueFunctionStatic (PrynPin *pin, void *data)
	{
		PrynFactoryClass *self = (PrynFactoryClass *) data;
		self->pinChangeValueFunction (pin);
	}

	static inline void pinConnectedFunctionStatic (PrynPin *pin, void *data)
	{
		PrynFactoryClass *self = (PrynFactoryClass *) data;
		self->pinConnectedFunction (pin);
	}

	static inline PrynString displayNameFunctionStatic (PrynComponent *component, void *data)
	{
		PrynFactoryClass *self = (PrynFactoryClass *) data;
		return self->displayNameFunction (component);
	}

	static inline void clickFunctionStatic (PrynComponent *component, void *data)
	{
		PrynFactoryClass *self = (PrynFactoryClass *) data;
		self->clickFunction (component);
	}
};
#endif /* __cplusplus */

/**	@} */ // (end of PrynFactory group)

#endif /* PRYN_FACTORY_H */
